/*
 * @Author: zhaoxiangyu@
 * @Date: 2021-11-28 18:54:54
 * @LastEditors: 647355931@qq.com
 * @LastEditTime_defaultValues18:56:13
 * @Description: file content
 */
// 存放form 表单组件的值 和一些存储
import { cloneDeep, isObject, isArray, isFunction } from 'loadsh';
import Regs from '../../utils/validate';
// eslint-disable-next-line no-unused-vars
export default class FormStore {
  // eslint-disable-next-line no-useless-constructor
  constructor(_defaultValues = {}, _rules = {}) {
    // 初始值 默认值 赋予给Value返回
    this.values = _defaultValues;
    // console.log('this.values ===> ', this.values);
    // 存放表单初始值，用于重置表单
    // console.log('_defaultValues ===> ', _defaultValues);
    this.defaultValues = cloneDeep(_defaultValues);
    // console.log('this.defaultValues ===> ', this.defaultValues);
    // 表单校验规则 如果有校验规则 会触发validate
    this.rules = _rules;
    // 事件回调
    // Field创建时，通过调用FormStore.subscribe(listener)订阅表单数据变动
    this.listeners = [];
  }

  // 订阅
  subscribe(listener) {
    // 如果订阅项已存在
    let isHas = this.listeners.indexOf(listener);
    if (isHas !== -1) {
      return;
    }
    // 不存在
    this.listeners.push(listener);
    // 返回一个用于取消订阅的函数 (触发后执行清除)
    return () => {
      this.listeners.splice(isHas, 1);
    };
  }
  // 表单验证错误信息收集
  errors = {};
  // 通知表单变动，调用所有listener
  notify(name) {
    // console.log('this.listeners ==> ', this.listeners);
    this.listeners.forEach((listener) => {
      //   console.log('listener ===> ', listener);
      listener(name);
    });
  }

  // 获取表单数据
  get(name) {
    // 如果没有传返回所有的默认值
    return name === undefined ? this.values : this.values[name];
  }

  // 设置表单数据 在set函数中调用 notify(name)，以保证所有的表单变动都会触发通知。
  set(name, value) {
    // console.log('value ===> ', value);
    //如果指定了name
    if (typeof name === 'string') {
      // 设置name对应的值
      this.values[name] = value;
      // 执行表单校验，见下
      this.validate(name);
      // 通知表单变动
      this.notify(name);
    } else {
      // 当传入一个object   { key: value, key2: value }  的时候
      const values = name;
      if (isObject(values)) {
        Object.keys(values).forEach((key) => this.set(key, values[key]));
      }
    }
  }

  // 重置表单
  // 重置表单值
  reset() {
    // 清空错误信息
    // this.errors = {};
    // 重置默认值
    // console.log('this.defaultValues ===> ', this.defaultValues);
    // console.log('this.values ===> ', this.values);
    this.values = cloneDeep(this.defaultValues);
    this.errors = {}
    // 执行通知
    this.notify('*');
  }

  // 表单校验
  // 1. FormStore构造函数中传入的rules是一个对象，该对象的键对应于表单域的name，值是一个校验函数。
  // 2. 校验函数参数接受表单域的值和整个表单值，返回boolean或string类型的结果。
  // 3. true代表校验通过。
  // 4. false和string代表校验失败，并且string结果代表错误信息。

  // 首先 validate 接受的是一个对象 { name: { validate: name || function, message: 'xxx'  } }
  validate(names) {
    // 这里要考虑两个地方, 1. 组件自带的校验规则 name  2. 自定义的校验函数
    // 2. 如果什么也没有传递 就校验所有
    if (!names) {
      // 校验完毕返回
      Object.keys(this.rules).forEach((n) => this.validate(n));
      // 执行通知
      // 返回所有错误信息
      // 返回一个包含第一条错误信息和表单值的数组
    } else {
      // 获取校验函数
      const validator = this.rules[names];
      // 自定义校验
      if (isFunction(validator)) {
        this.funValidate(names, validator);
      }
      // 内置校验
      if (isArray(validator)) {
        this.objValidate(names, validator);
      }
    }
    // 验证完 通知页面更改
    this.notify('*');
    return this.errors;
  }

  error(name, value) {
    // 如果什么都没传递
    if (!arguments.length) {
      return;
    }

    this.errors[name] = value;

    // 返回错误信息
    return this.errors[name];
  }

  funValidate(names, valid) {
    // 根据name 获取表单值
    const value = this.get(names);
    // 校验结果
    // value 初始化的时候为空 所以要给一个默认值
    const result = valid ? valid(value || '', this.values) : true;
    // result 为 true 表示校验通过 不记录数据
    // result 为 false 或者 错误信息 表示验证失败 需要记录返回
    // console.log('result ===> ', result);
    if (!result || typeof result === 'string') {
      this.error(names, result);
    } else {
      //   console.log(`${names} 验证通过`);
      // 验证通过
      delete this.errors[names];
    }
  }
  objValidate(names, obj) {
    obj.forEach((v) => {
      const { validate, message } = v;
      // 根据name 获取表单值
      const value = this.get(names);
      let keys = Regs[validate];
      if (!keys) {
        this.error(validate, `${validate} 非内置的校验规则`);
      } else {
        if (!keys.test(value)) {
          this.error(names, message || `${names} 校验未通过`);
        } else {
          // 验证通过
          delete this.errors[names];
        }
      }
    });
  }
}
